package com.yifeng.repo.micro.service.server.auth.sso.filter;

import com.gomcarter.frameworks.base.common.RequestUtils;
import com.gomcarter.frameworks.base.json.JsonError;
import com.yifeng.repo.base.utils.converter.JacksonHelper;
import com.yifeng.repo.micro.service.server.auth.sso.api.SsoClient;
import com.yifeng.repo.micro.service.server.auth.sso.filter.cache.DegradableCache;
import com.yifeng.repo.micro.service.server.auth.sso.filter.context.SsoAuthInfo;
import com.yifeng.repo.micro.service.server.auth.sso.filter.context.SsoContext;
import lombok.extern.slf4j.Slf4j;
import next.fire.sso.api.model.Menu;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;

import static com.talkyun.utils.MD5.md5;

@Slf4j
public class SsoFilter implements Filter {
    private final DegradableCache cache;
    private final SsoClient ssoClient;
    private final List<String> patterns;
    private final List<String> ignorePatterns;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 为了兼容8.x版本的tomcat, 必须实现init方法，并且不要使用父类的实现.Filter.super.init(filterConfig);
    }

    public SsoFilter(DegradableCache degradableCache, SsoClient ssoClient, List<String> patterns, List<String> ignorePatterns) {
        this.cache = degradableCache;
        this.ssoClient = ssoClient;
        this.patterns = patterns;
        this.ignorePatterns = ignorePatterns;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        SsoAuthInfo ssoAuthInfo = SsoContext.getOrAdd(httpServletRequest);
        String mapping = httpServletRequest.getRequestURI();
        if (mapping.endsWith("/") || match(ignorePatterns, mapping)) {
            chain.doFilter(request, response);
        } else {
            if (match(patterns, mapping)) {
                if(ssoAuthInfo == null || ssoAuthInfo.getToken() == null) {
                    printError(response, "鉴权失败, sso鉴权信息不完整");
                    return;
                }
                String deviceValue = ssoAuthInfo.getToken().getDevice();
                String tokenValue = ssoAuthInfo.getUserToken();
                String ip = RequestUtils.getRemoteAddress(httpServletRequest);
                String resource = httpServletRequest.getRequestURI();
                String md5 = md5(ip + ":" + tokenValue + ":" + resource);
                Object val = cache.get(md5, () -> isAllowed(deviceValue, tokenValue, resource));
                boolean allow = val != null && (Boolean) val;
                if (!allow) {
                    printError(response, "鉴权失败, token信息不匹配或已过期");
                    return;
                }

            }
            chain.doFilter(request, response);
        }
        SsoContext.clear();
    }

    @Override
    public void destroy() {

    }

    private boolean isAllowed(String device, String token, String resource) {
        Menu menu = new Menu();
        menu.setMenuHref(resource);
        return ssoClient.isAllowAccess(device, token, menu);
    }

    private boolean match(List<String> patterns, String mapping) {
        if (patterns.isEmpty()) {
            return false;
        }
        PathMatcher pathMatcher = new AntPathMatcher();
        for (String pattern : patterns) {
            if (pathMatcher.match(pattern, mapping)) {
                return true;
            }
        }
        return false;
    }

    private void printError(ServletResponse response, String msg) throws IOException {
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json");
        JsonError jsonError = new JsonError();
        jsonError.setCode(-1);
        jsonError.setData(null);
        jsonError.setMessage(msg);
        response.getWriter().print(JacksonHelper.toJson(jsonError));
    }
}
